﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Jacobi.ComponentModel.Decomposition
{
    /// <summary>
    /// This class processes a collection of filters.
    /// </summary>
    public partial class PipesAndFiltersEngine
    {
        private OperationCollection _operations;
        private TaskFactory _taskFactory;
        private CancellationTokenSource _cancellationToken;
        private List<Task> _tasks;

        /// <summary>
        /// Constructs a new engine based on a collection of filters.
        /// </summary>
        /// <param name="operations">The collection of filters to process. Must not be null.</param>
        public PipesAndFiltersEngine(OperationCollection operations)
        {
            _operations = operations;
            _cancellationToken = new CancellationTokenSource();
            _taskFactory = new TaskFactory(_cancellationToken.Token, TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness, 
                TaskContinuationOptions.LongRunning | TaskContinuationOptions.PreferFairness, TaskScheduler.Default);
            _tasks = new List<Task>();
        }

        /// <summary>
        /// Starts the processing engine.
        /// </summary>
        /// <remarks>Allocates Threads.</remarks>
        public void Start()
        {
            foreach (IOperation operation in _operations)
            {
                Task task = _taskFactory.StartNew(TaskAction, new TaskData(operation, _cancellationToken.Token));
                _tasks.Add(task);
            }
        }

        /// <summary>
        /// Cancels processing of the engine.
        /// </summary>
        /// <remarks>Any exceptions will be thrown here.</remarks>
        public void Cancel()
        {
            _cancellationToken.Cancel(false);

            Task.WaitAll(_tasks.ToArray());
            _tasks.Clear();
        }

        private void TaskAction(object state)
        {
            ExecuteOperation((TaskData)state);
        }

        private void ExecuteOperation(TaskData taskData)
        {
            try
            {
                while (!taskData.CancellationToken.IsCancellationRequested)
                {
                    taskData.Operation.Process();
                }
            }
            catch (Exception)
            {
                _cancellationToken.Cancel();

                throw;
            }
        }
    }
}
